//
//  ORMDb.m
//  storeapp
//
//  Created by huangyi on 13-4-25.
//  Copyright (c) 2013年 ios. All rights reserved.
//

#import "ORMDatabase.h"
#import "ORMObject.h"

#import "FMDatabase.h"
#import "FMDatabaseAdditions.h"
#import "FMResultSet.h"

#import "NSArray+utils.h"
#import "NSString+utils.h"

@interface ORMDatabase ()

@property(nonatomic,strong)FMDatabase *database;

@end

@implementation ORMDatabase

-(void)dealloc{
    [self close];
}

-(id)initWithDatabase:(NSString*)dbfile{
    self=[super init];
    if (self) {
        FMDatabase *db=[FMDatabase databaseWithPath:dbfile];
        self.database=db;
        [self open];
    }
    return self;
}

-(BOOL)open{
    return [self.database open];
}
-(BOOL)close{
    return [self.database close];
}

-(NSArray*)getSchema{
    NSMutableArray *array=[NSMutableArray array];
    FMResultSet *set=[self.database getSchema];
    while ([set next]) {
        [array addObject:set.resultDictionary];
    }
    return [NSArray arrayWithArray:array];
}
-(NSArray*)listOfTables{
    NSArray *schema=[self getSchema];
    NSMutableArray *array=[NSMutableArray array];
    for (NSDictionary *desc in schema) {
        if ([desc[@"type"] isEqualToString:@"table"]) {
            [array addObject:desc[@"name"]];
        }
    }
    return [NSArray arrayWithArray:array];
}
-(NSArray*)getTableSchema:(NSString*)table{
    NSMutableArray *array=[NSMutableArray array];
    FMResultSet *set=[self.database getTableSchema:table];
    while ([set next]) {
        [array addObject:set.resultDictionary];
    }
    return [NSArray arrayWithArray:array];
}
-(NSArray*)listOfFields:(NSString*)table{
    NSArray *schema=[self getTableSchema:table];
    NSMutableArray *array=[NSMutableArray array];
    for (NSDictionary *desc in schema) {
        [array addObject:desc[@"name"]];
    }
    return [NSArray arrayWithArray:array];
}

//-(ORMTable*)getTable:(NSString*)table{
//    ORMTable *t=[[ORMTable alloc] init];
//    t.dbContext=self;
//    t.tableName=table;
//    return t;
//}

-(int)count:(NSString*)table{
    int row=0;
    FMDatabase *db=self.database;
    NSString *sql=[NSString stringWithFormat:@"SELECT COUNT(id) FROM %@",table];
    FMResultSet *set=[db executeQuery:sql];
    while ([set next]) {
        NSNumber *num=[set objectForColumnIndex:0];
        row=num.intValue;
    }
    return row;
}

-(ORMObject*)dispense:(NSString*)table{
    ORMObject *obj=[[ORMObject alloc] init];
    obj.rowid=InvalidRowId;
    obj.table=table;
    obj.database=self;
    return obj;
}
-(ORMObject*)dispense:(NSString *)table ifNotExist:(int)rowid{
    ORMObject* obj=[self load:table rowid:rowid];
    if (obj==nil) {
        obj=[self dispense:table];
        obj[@"id"]=@(rowid);
    }
    return obj;
}
-(ORMObject*)load:(NSString*)table rowid:(int)rowid{
    ORMObject *obj=[[ORMObject alloc] init];
    obj.rowid=rowid;
    obj.table=table;
    obj.database=self;
    FMDatabase *db=self.database;
    NSString *sql=[NSString stringWithFormat:@"SELECT * FROM %@ WHERE id=%i",table,rowid];
    FMResultSet *set=[db executeQuery:sql];
    NSDictionary *row=nil;
    if ([set next]) {
        row=[set resultDictionary];
        [obj setValuesForKeysWithDictionary:row];
    }else{
        obj=nil;
    }
    return obj;
}
-(ORMObject*)findOne:(NSString*)table match:(NSString*)where,...{
    NSString *condition=nil;
	va_list args;
	if (where)
	{
		va_start(args, where);
        condition=[[NSString alloc] initWithFormat:where arguments:args];
		va_end(args);
	}
    ORMObject *obj=nil;
    FMDatabase *db=self.database;
    NSString *sql=[NSString stringWithFormat:@"SELECT id FROM %@ WHERE %@",table,condition];
    FMResultSet *set=[db executeQuery:sql];
    if ([set next]) {
        NSNumber *rowid=[set objectForColumnIndex:0];
        obj=[self load:table rowid:rowid.intValue];
    }
    return obj;
}
-(NSArray*)findAll:(NSString*)table match:(NSString*)where,...{
    NSString *condition=nil;
	va_list args;
	if (where)
	{
		va_start(args, where);
        condition=[[NSString alloc] initWithFormat:where arguments:args];
		va_end(args);
	}
    NSMutableArray *array=[NSMutableArray array];
    FMDatabase *db=self.database;
    NSString *sql=[NSString stringWithFormat:@"SELECT id FROM %@ WHERE %@",table,condition];
    FMResultSet *set=[db executeQuery:sql];
    if ([set next]) {
        NSNumber *rowid=[set objectForColumnIndex:0];
        ORMObject *obj=[self load:table rowid:rowid.intValue];
        if (obj) {
            [array addObject:obj];
        }
    }
    return [NSArray arrayWithArray:array];
}

-(int)store:(ORMObject*)object{
    int rowid=InvalidRowId;
    [self alterTable:object];
    FMDatabase *db=self.database;
    if (object.rowid!=InvalidRowId) {
        NSMutableArray *set=[NSMutableArray array];
        NSArray *keys=[object allKeys];
        for (NSString *key in keys) {
            if (![key isEqualToString:@"id"]) {
                NSString *value=object[key];
                [set addObject:[NSString stringWithFormat:@"%@='%@'",key,value]];
            }
        }
        if (set.count) {
            NSString *sql=[NSString stringWithFormat:@"UPDATE %@ SET %@ WHERE id=%i",object.table,[set componentsJoinedByString:@","],object.rowid];
            if ([db executeUpdate:sql]) {
                rowid=object.rowid;
            }else{
            }
        }
    }else{
        NSMutableDictionary *set=[NSMutableDictionary dictionary];
        NSArray *keys=[object allKeys];
        NSArray *values=[object allValues];
        [set setValuesForKeysWithDictionary:[object dump]];
        NSString *sql=[NSString stringWithFormat:@"INSERT INTO %@ (%@) VALUES('%@')",object.table,[keys componentsJoinedByString:@","],[values componentsJoinedByString:@"','"]];
        if ([db executeUpdate:sql]) {
            rowid=[db lastInsertRowId];
            object.rowid=rowid;
        }else{
        }
    }
    return rowid;
}

-(BOOL)trash:(ORMObject*)object{
    BOOL result=YES;
    if (object.rowid!=InvalidRowId) {
        FMDatabase *db=self.database;
        NSString *sql=[NSString stringWithFormat:@"DELETE FROM %@ WHERE id=%i",object.table,object.rowid];
        result=[db executeUpdate:sql];
    }
    return result;
}

-(NSDictionary*)dump:(ORMObject*)object{
    return [object dump];
}

-(BOOL)wipe:(NSString*)table{
    FMDatabase *db=self.database;
    NSString *sql=[NSString stringWithFormat:@"DROP TABLE %@",table];
    return [db executeUpdate:sql];
}
-(BOOL)optimize{
    FMDatabase *db=self.database;
    NSString *sql=@"VACUUM";
    return [db executeUpdate:sql];
}

-(BOOL)alterTable:(ORMObject*)object{
    BOOL result=YES;
    FMDatabase *db=self.database;
    [db beginTransaction];
    NSString *tableName=object.table;
    NSArray *tables=[self listOfTables];
    if (![tables containsString:tableName]) {
        NSString *sql=[NSString stringWithFormat:@"CREATE TABLE IF NOT EXISTS '%@' ('id' INTEGER PRIMARY KEY AUTOINCREMENT)",tableName];
        result=[db executeUpdate:sql];
    }
    if (result) {
        NSArray *fields=[self listOfFields:tableName];
        NSArray *keys=[object allKeys];
        for (NSString *key in keys) {
            if (![fields containsString:key]) {
                id value=object[key];
                NSString *type=@"TEXT";
                if ([value isKindOfClass:[NSNumber class]]) {
                    type=@"REAL";
                }
                NSString *sql=[NSString stringWithFormat:@"ALTER TABLE %@ ADD COLUMN '%@' %@",tableName,key,type];
                result=[db executeUpdate:sql];
            }
        }
    }else{
    }
    [db commit];
    return result;
}

@end
